{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "a7178d06",
   "metadata": {},
   "source": [
    "### 函数作为返回值"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6183d511",
   "metadata": {},
   "source": [
    "高阶函数除了可以接收函数作为参数外，还可以把函数作为结果值返回。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "b8163b42",
   "metadata": {},
   "outputs": [],
   "source": [
    "def lazy_sum(*args):\n",
    "    def sum():\n",
    "        ax = 0\n",
    "        for n in args:\n",
    "            ax = n + ax\n",
    "        return ax\n",
    "    return sum"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "421c694a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<function __main__.lazy_sum.<locals>.sum()>"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "f = lazy_sum(1,3,5,7,9)\n",
    "f"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aac5cadd",
   "metadata": {},
   "source": [
    "调用f时，才真正计算求和"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "a0aa822d",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "25"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "f()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4a213320",
   "metadata": {},
   "source": [
    "上面例子中，lazy_sum中定义了函数sum，内部函数sum可以引用外部函数lazy_sum的参数和局部变量。\n",
    "当lazy_sum返回函数sum时，相关参数和变量都保存在返回的函数中，这种称为闭包的程序结构拥有巨大的威力"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d29663a7",
   "metadata": {},
   "source": [
    "### 闭包"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "312af46c",
   "metadata": {},
   "source": [
    "当一个函数返回了一个函数后，其内部的局部变量还被新函数引用。\n",
    "返回的函数没有立刻执行，直到调用后f()才执行"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "412950bc",
   "metadata": {},
   "outputs": [],
   "source": [
    "def count():\n",
    "    fs = []\n",
    "    for i in range(1,4):\n",
    "        def f():\n",
    "            return i*i\n",
    "        fs.append(f)\n",
    "    return fs\n",
    "f1, f2, f3 = count()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "9126dfc1",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(9, 9, 9)"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "f1(),f2(),f3()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "044a4638",
   "metadata": {},
   "source": [
    "上面的结果不是1，4，9，原因在于返回函数引用了变量i，但它并非立刻执行。等3个函数都返回时，他们所引用i都已经变成了3"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "faa34a7e",
   "metadata": {},
   "source": [
    "因此，返回闭包时，返回函数不要引用任何循环变量，或者后续会发生变化的变量"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "0e48b43e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def count():\n",
    "    def f(j):\n",
    "        def g():\n",
    "            return j * j\n",
    "        return g\n",
    "    fs = []\n",
    "    for i in range(1,4):\n",
    "        fs.append(f(i))\n",
    "    return fs\n",
    "f1, f2 ,f3= count()\n",
    "f1()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d903e2cf",
   "metadata": {},
   "source": [
    "### nonlocal"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "06b133ef",
   "metadata": {},
   "source": [
    "使用闭包，就是内层函数引用了外层函数的局部变量，如果只是读外层变量的值，我们会发现返回的闭包函数调用一切正常。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "c1feb2cd",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def inc():\n",
    "    x = 0\n",
    "    def fn():\n",
    "        return x + 1\n",
    "    return fn\n",
    "f = inc()\n",
    "f()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e486dcac",
   "metadata": {},
   "source": [
    "如果对外层变量赋值，python解释器会把x当作函数fn的局部变量，就会报错："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "b5f81c4b",
   "metadata": {},
   "outputs": [
    {
     "ename": "UnboundLocalError",
     "evalue": "local variable 'x' referenced before assignment",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mUnboundLocalError\u001b[0m                         Traceback (most recent call last)",
      "\u001b[1;32m~\\AppData\\Local\\Temp/ipykernel_16320/700712112.py\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m      7\u001b[0m     \u001b[1;32mreturn\u001b[0m \u001b[0mfn\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      8\u001b[0m \u001b[0mf\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0minc\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 9\u001b[1;33m \u001b[0mf\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;32m~\\AppData\\Local\\Temp/ipykernel_16320/700712112.py\u001b[0m in \u001b[0;36mfn\u001b[1;34m()\u001b[0m\n\u001b[0;32m      3\u001b[0m     \u001b[1;32mdef\u001b[0m \u001b[0mfn\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      4\u001b[0m         \u001b[1;31m#nonlocal x\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 5\u001b[1;33m         \u001b[0mx\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mx\u001b[0m \u001b[1;33m+\u001b[0m \u001b[1;36m1\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m      6\u001b[0m         \u001b[1;32mreturn\u001b[0m \u001b[0mx\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      7\u001b[0m     \u001b[1;32mreturn\u001b[0m \u001b[0mfn\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mUnboundLocalError\u001b[0m: local variable 'x' referenced before assignment"
     ]
    }
   ],
   "source": [
    "def inc():\n",
    "    x = 0\n",
    "    def fn():\n",
    "        #nonlocal x\n",
    "        x = x + 1\n",
    "        return x\n",
    "    return fn\n",
    "f = inc()\n",
    "f()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e823d10e",
   "metadata": {},
   "source": [
    "上面报错的原因，就是x作为局部变量没有初始化，直接计算x+1时不行。\n",
    "可以在fn()函数内部加上一个nonlocal x的声明。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "e1614495",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def inc():\n",
    "    x = 0\n",
    "    def fn():\n",
    "        nonlocal x\n",
    "        x = x + 1\n",
    "        return x\n",
    "    return fn\n",
    "f = inc()\n",
    "f()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3769884e",
   "metadata": {},
   "source": [
    "### 小结"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2dd6d6b2",
   "metadata": {},
   "source": [
    "一个函数可以返回一个计算结果，也可以返回一个函数\n",
    "返回一个函数时，该函数并未执行，返回函数中不要引用任何可能会变化的变量"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dc6e5789",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
